<?php
/**
 * Created by PhpStorm.
 * User: yann
 * Date: 2019/4/22
 * Time: 下午11:41
 */

namespace App\Services;


use App\Contracts\AuthContract;
use App\Models\AuthGroup;
use App\Models\AuthGroupAccess;
use App\Models\AuthRule;
use App\Models\Menu;
use Illuminate\Support\Facades\DB;

class AuthService implements AuthContract
{
    public function getGroup(int $id): AuthGroup
    {
        return AuthGroup::findOrFail($id);
    }

    public function getGroups(int $page, int $size): array
    {
        $query =  AuthGroup::query();
        $count = $query->count();
        $list = $query->offset(($page - 1)* $size)->limit($size)->get()->toArray();
        return [
            'count' => $count,
            'list' => $list
        ];
    }

    public function saveGroup(array $data): bool
    {
        $rules = [];
        if ($data['rules']) {
            $rules = $data['rules'];
            $rules = array_filter($rules);
        }
        unset($data['rules']);
        Db::beginTransaction();
        try{
            $auth = AuthGroup::create([
                'name'=>$data['name'],
                'description'=>$data['description'],
                'status' => 1
            ]);
            if ($rules) {
                $insertData = [];
                foreach ($rules as $value) {
                    if ($value) {
                        $insertData[] = [
                            'group_id' => $auth->id,
                            'route'     => $value
                        ];
                    }
                }
                AuthRule::insert($insertData);
            }
            Db::commit();
            return true;
        }catch (\Exception $e){
            Db::rollback();
            throw $e;
        }
    }

    public function updateGroup(array $data, AuthGroup $authGroup): bool
    {
        //更新AuthGroup 更新AuthRule
        Db::beginTransaction();
        try{
            $authGroup->update($data);

            if(isset($data['rules'])){
                $rules = array_filter($data['rules']);
                $has = AuthRule::select('id','route')->where('group_id',$data['id'])->get()->toArray();
                $hasRule = array_column($has, 'route');
                $needDel = array_flip($hasRule);
                $needAdd = [];
                foreach ($rules as $key => $value) {
                    if (!empty($value)) {
                        if (!in_array($value, $hasRule)) {
                            $needAdd[] = [
                                'route' => $value,
                                'group_id' => $data['id']
                            ];
                        } else {
                            unset($needDel[$value]);
                        }
                    }
                }
                if (count($needAdd)) {
                    AuthRule::insert($needAdd);
                }
                if (count($needDel)) {
                    $urlArr = array_keys($needDel);
                    AuthRule::where('group_id','=',$data['id'])
                            ->whereIn('route',$urlArr)
                            ->delete();
                }
            }
            Db::commit();
            return true;
        }catch (\Exception $e){
            Db::rollBack();
            throw $e;
        }
    }

    public function deleteGroup(int $id): bool
    {
        Db::beginTransaction();
         try{
             if(AuthGroup::destroy($id)){
                 AuthRule::where('group_id',$id)->delete();
             }
             Db::commit();
             return true;
         }catch (\Exception $e){
             Db::rollBack();
             throw $e;
         }
    }

    public function getRules(int $group_id = 0): array
    {
        try{
            $list = Menu::get()->toArray();
            $list = self::listToTree($list);
            $rules = [];
            if ($group_id) {
                $rules = AuthRule::where('group_id',$group_id)->get()->toArray();
                $rules = array_column($rules, 'route');
            }
            $newList = self::buildList($list, $rules);
            return $newList;
        }catch (\Exception $e){
            throw $e;
        }
    }

    public function deleteUserFromGroup(int $userId, int $groupId): bool
    {
        $model = AuthGroupAccess::where('uid',$userId)->first();
        $oldGroupArr = explode(',', $model['group_id']);
        $key = array_search($groupId, $oldGroupArr);
        unset($oldGroupArr[$key]);
        $newData = implode(',', $oldGroupArr);
        $model->group_id = $newData;
        return $model->save();
    }

    public function getMenu(int $id): Menu
    {
        return Menu::findOrFail($id);
    }

    public function getMenus(): array
    {
        $list = Menu::get()->toArray();
        return self::formatTree(self::listToTree($list));
    }

    public function saveMenu(array $data): bool
    {
        if(Menu::create($data)){
            return true;
        } else {
            return false;
        }
    }

    public function updateMenu(array $data, Menu $menu = null): bool
    {
        if(!$menu) {
            $menu = Menu::query();
        }
        return $menu->update($data);
    }

    public function deleteMenu(int $id): bool
    {
        return Menu::destroy($id);
    }

    public static function formatTree($list, $lv = 0, $title = 'name'){
        $formatTree = array();
        foreach($list as $key => $val){
            $title_prefix = '';
            for( $i=0;$i<$lv;$i++ ){
                $title_prefix .= "|---";
            }
            $val['lv'] = $lv;
            $val['namePrefix'] = $lv == 0 ? '' : $title_prefix;
            $val['showName'] = $lv == 0 ? $val[$title] : $title_prefix.$val[$title];
            if(!array_key_exists('_child', $val)){
                array_push($formatTree, $val);
            }else{
                $child = $val['_child'];
                unset($val['_child']);
                array_push($formatTree, $val);
                $middle = self::formatTree($child, $lv+1, $title); //进行下一层递归
                $formatTree = array_merge($formatTree, $middle);
            }
        }
        return $formatTree;
    }

    private function listToTree($list, $pk='id', $pid = 'pid', $child = '_child', $root = '0') {
        $tree = array();
        if(is_array($list)) {
            $refer = array();
            foreach ($list as $key => $data) {
                $refer[$data[$pk]] = &$list[$key];
            }
            foreach ($list as $key => $data) {
                $parentId =  $data[$pid];
                if ($root == $parentId) {
                    $tree[] = &$list[$key];
                }else{
                    if (isset($refer[$parentId])) {
                        $parent = &$refer[$parentId];
                        $parent[$child][] = &$list[$key];
                    }
                }
            }
        }
        return $tree;
    }

    private function buildList($list, $rules) {
        $newList = [];
        foreach ($list as $key => $value) {
            $newList[$key]['title'] = $value['name'];
            $newList[$key]['key'] = $value['route'];
            if (isset($value['_child'])) {
                $newList[$key]['expand'] = true;
                $newList[$key]['children'] = self::buildList($value['_child'], $rules);
            } else {
                if (in_array($value['route'], $rules)) {
                    $newList[$key]['checked'] = true;
                }
            }
        }

        return $newList;
    }

}
